package main;

import java.applet.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;

import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;

/**
 * Class that act as JPanel to BounceBall Class (which is JFrame).
 */
public class GameDisplay extends JPanel {
    /**
     * timer for refreshing the frame and moving objects.
     */
    private Timer timer;
    /**
     * Creating a Paddle Object.
     */
    private Paddle paddle;
    /**
     * Creating a Ball Object.
     */
    private Ball ball;
    /**
     * Creating a Brick Object.
     */
    private Brick brick;
    /** Frame objects */
    private GameFrame frameUp;
    private GameFrame frameDown;
    private GameFrame frameLeft;
    private GameFrame frameRight;
    /** Collision detection system. */
    private CollisonDetector detector;
    private boolean brickDestroyed;
    private int gameWidth;
    private int gameHeight;
    /** the millisecond form epoch. */
    private long lastFrameTime;
    /** Calendar for time calculation. */
    private Calendar cal;

    /**
     * Constructor helps to create Ball, Paddle Component.
     * @param width of game panel
     * @param height of game panel
     */
    public GameDisplay(final int width, final int height) {
        gameWidth = width;
        gameHeight = height;
        this.setSize(width, height);
        detector = new CollisonDetector();
        initGame();
        addKeyListener(new KeyBoardAdapter());
        setFocusable(true);
    }
    /**
     * Initialize the game.
     */
    private void initGame() {
        // Regenerate objects
        detector.clearAllObjects();
        createFrame(gameWidth, gameHeight);
        paddle = new Paddle(100.0, 150.0, 20.0, 80.0);
        ball = new Ball(100, 100, 10, 10);
        brick = new Brick(20, 20, 25, 25);
        detector.addObject(ball);
        detector.addObject(brick);
        detector.addObject(frameUp);
        detector.addObject(frameDown);
        detector.addObject(frameRight);
        detector.addObject(frameLeft);
        detector.addObject(paddle);
        // Reset last frame time.
        cal = Calendar.getInstance();
        lastFrameTime = cal.getTimeInMillis();
        // Set new speed and location for ball and brick
        Random rdm = new Random();
        ball.setPosition(new Point2D.Double(100, 100));
        ball.setSpeed(new Point2D.Double(40 + rdm.nextInt(160), 40 + rdm
                .nextInt(160)));
        brick.setPosition(new Point2D.Double(20 + rdm.nextInt(gameWidth - 40),
                20 + rdm.nextInt(gameHeight - 40)));
        brickDestroyed = false;
        // Reset Timer
        timer = new Timer();
        timer.scheduleAtFixedRate(new Schedular(), 1000, 30);
    }
    /**
     * Create frame to hold balls.
     * @param width of panel
     * @param height of panel
     */
    private void createFrame(final int width, final int height) {
        /** make down and right frame visible.*/
        int frameOffset = 3;
        frameUp = new GameFrame(0.0, 0.0, width, 2.0);
        frameLeft = new GameFrame(0.0, 0.0, 2.0, height);
        frameDown = new GameFrame(0.0, height - frameOffset, width, 2.0);
        frameRight = new GameFrame(width - frameOffset, 0.0, 2.0, height);
    }
    @Override
    public final void paint(final Graphics g) {
        super.paint(g);
        // Calculate the millisecond time form last frame. In second.
        cal = Calendar.getInstance();
        long crtTime = cal.getTimeInMillis();
        double delta = (crtTime - lastFrameTime) / 1000.0;
        lastFrameTime = crtTime;
        if (!brickDestroyed) {
            // We are in game
            //Save all objects current position.
            detector.saveLastPosition();
            //Update the position of all objects according to their speed.
            detector.updateAllPosition((float) delta);
            ArrayList<Pair> collisionList = detector.doCollision();
            for (Pair pair : collisionList) {
                // Let fast object handles collision response.
                pair.getFirst().onHit(pair.getSecond());
                // Test if the ball hits bricks
                if (pair.getFirst() instanceof Ball
                        && pair.getSecond() instanceof Brick) {
                    brickDestroyed = true;
                    detector.removeObject(pair.getSecond());
                }
            }
            drawAll(g);
        } else { // Restart Game
            initGame();
        }
        g.dispose();
    }

    /**
     * Draw objects in game.
     *
     * @param g
     *            Graphic
     */
    private void drawAll(final Graphics g) {
        ball.paint(g);
        paddle.paint(g);
        brick.paint(g);
        drawFrame(g);
    }
    /**
     * Draw Frame.
     * @param g graphic
     */
    private void drawFrame(final Graphics g) {
        frameUp.paint(g);
        frameDown.paint(g);
        frameLeft.paint(g);
        frameRight.paint(g);
    }

    /**
     * KeyBoardAdapter extends KeyAdapter. KeyAdapter Class exists as
     * convenience for creating Listener Objects.
     */
    private class KeyBoardAdapter extends KeyAdapter {
        /**
         * Overriding keyReleased Event of KeyAdapter.
         *
         * @param e
         *            : KeyEvent
         */
        public void keyReleased(final KeyEvent e) {
            paddle.keyReleased(e);
        }

        /**
         * Overriding keyPressed Event of KeyAdapter.
         *
         * @param e
         *            : KeyEvent
         */
        public void keyPressed(final KeyEvent e) {
            paddle.keyPressed(e);
        }
    }

    /**
     * A schedular which extends TimerTask.
     */
    class Schedular extends TimerTask {
        /**
         * overriding abstract method run of TimerTask.
         */
        public void run() {
            repaint();
        }
    }
}
